/** @file    VGettyAMBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: VGettyAMBundle.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::devices::VGettyAMBundle class.
 * The header for this class can be found in VGettyAMBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "VGettyAMBundle.h"
 #include "VGettyAMImpl.h"

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/Fact.h>
 #include <core/Table.h>
 #include <core/ThreadManager.h>
 #include <core/DeviceManager.h>
 #include <core/SecurityManager.h>
 #include <core/UserManager.h>
 #include <core/SystemError.h>
 #include <core/WoshKernel.h>

 #include <core/MessageFilter.h>
 #include <core/MessageProcessingCommon.h>


using namespace wosh::communication;
namespace wosh {
 namespace devices {

 WOSH_REGISTER(wosh::devices::VGettyAMBundle, "wosh::interfaces::services::CommunicatorService", _VGettyAM_VERSION, _static_VGettyAMBundle1 )
 WOSH_REGISTER(wosh::devices::VGettyAMBundle, "wosh::interfaces::devices::VGettyAM", _VGettyAM_VERSION, _static_VGettyAMBundle2 )
 WOSH_REGISTER(wosh::devices::VGettyAMBundle, "wosh::BundleGeneric", _VGettyAM_VERSION, _static_VGettyAMBundle2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

VGettyAMBundle::VGettyAMBundle()
	: BundleGeneric() {
	BundleGeneric::setName( _VGettyAM_NAME, false );
	Properties.update( _KEY_Version, _VGettyAM_VERSION );

	Log(LOG_DEBUG, " Configuring VGettyAM worker.." );
	this->devWorker = new VGettyAMImpl(*this);
	this->devWorker->setVGettyAMListener(this);

	this->devWorker->setDialScript( _VGettyAM_DEFAULT_ScriptDial );
	this->devWorker->setCtrlScript( _VGettyAM_DEFAULT_ScriptControl );
	this->devWorker->setFolderMessageStored( _VGettyAM_DEFAULT_FolderStored );
	this->devWorker->setFolderMessageIncoming( _VGettyAM_DEFAULT_FolderIncoming );
	this->devWorker->setMessageGreeting( _VGettyAM_DEFAULT_MessageGreeting );
	this->devWorker->setBindAddressSlave( _VGettyAM_DEFAULT_BindAddressSlave );
	this->devWorker->setBindAddressDevice( _VGettyAM_DEFAULT_BindAddressDevice );

	this->devState = wosh::interfaces::Device::UNKNOWN;

Log.setLevel(LOG_VERBOSE);

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _CommunicatorService_KEY_ChannelType,	(int)Notification::TYPE_AUDIO, Permission(Permission::Read) );
	Properties.set( _CommunicatorService_KEY_ChannelLink,	(int)Communicator::LINK_SYNCH, Permission(Permission::Read) );
	Properties.set( _CommunicatorService_KEY_Cost,			0.2,Permission::RW_R__R__() );
	Properties.set( _CommunicatorService_KEY_Priority,		50,	Permission::RW_RW_R__() );
	Properties.set( _CommunicatorService_KEY_Reliability,	90,	Permission::RW_RW_R__() );
	Properties.set( _CommunicatorService_KEY_Protocol,		(int)Notification::PROTOCOL_HUMAN,	Permission::RW_R__R__() );
	Properties.set( _CommunicatorService_KEY_Locations,		"",	Permission::RW_R__R__() );

	Properties.set( _Device_KEY_DeviceStatus, wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::UNKNOWN), Permission( Permission::Read ) );

	Properties.set( _Phone_KEY_PhoneNumber,			"", Permission( Permission::Read ) );
	Properties.set( _Phone_KEY_PhoneLinesNumber,	1, Permission( Permission::Read ) );

	Properties.set( _VGettyAM_KEY_RefreshFrequency,	60,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_ScriptDial,		_VGettyAM_DEFAULT_ScriptDial,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_ScriptControl,	_VGettyAM_DEFAULT_ScriptControl	,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_FolderStored,		_VGettyAM_DEFAULT_FolderStored,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_FolderIncoming,	_VGettyAM_DEFAULT_FolderIncoming,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_MessageGreeting,	_VGettyAM_DEFAULT_MessageGreeting,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_BindAddressSlave,	_VGettyAM_DEFAULT_BindAddressSlave,	Permission::RW_RW_R__() );
	Properties.set( _VGettyAM_KEY_BindAddressDevice,_VGettyAM_DEFAULT_BindAddressDevice,	Permission::RW_RW_R__() );
	Properties.monitor(_VGettyAM_KEY_RefreshFrequency);
	Properties.monitor(_VGettyAM_KEY_ScriptDial);
	Properties.monitor(_VGettyAM_KEY_ScriptControl);
	Properties.monitor(_VGettyAM_KEY_FolderStored);
	Properties.monitor(_VGettyAM_KEY_FolderIncoming);
	Properties.monitor(_VGettyAM_KEY_MessageGreeting);
	Properties.monitor(_VGettyAM_KEY_BindAddressSlave);
	Properties.monitor(_VGettyAM_KEY_BindAddressDevice);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodRequest* mmForceState = Methods.create<MethodRequest>( _VGettyAM_METHOD_force_state );
	mmForceState->setMethod( this, (MethodRequestPtr)&VGettyAMBundle::mmDoForceState );
	mmForceState->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );

	MethodWRESULT* mmDeclareCommunicator = Methods.create<MethodWRESULT>( _CommunicatorService_METHOD_declare, "declare as Communicator" );
	mmDeclareCommunicator->setMethod( this, (MethodWRESULTPtr)&VGettyAMBundle::declareCommunicator );
	mmDeclareCommunicator->getPermission().setMask( Permission::RX, Permission::RX, Permission::Read );

	MethodMessageResponse* mmSendMessageToUser = Methods.create<MethodMessageResponse>( _CommunicatorService_METHOD_sendmessage, "send message to (wosh) username" );
	mmSendMessageToUser->setMethod( this, (MethodMessageResponsePtr)&VGettyAMBundle::mmDoSendMessageToUser );
	mmSendMessageToUser->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );

	MethodMessageResponse* mmDialCall = Methods.create<MethodMessageResponse>( _Phone_METHOD_dial );
	mmDialCall->setMethod( this, (MethodMessageResponsePtr)&VGettyAMBundle::mmDoDialCall );
	mmDialCall->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );

	MethodMessageResponse* mmDialCallAndPlay = Methods.create<MethodMessageResponse>( _VGettyAM_METHOD_dialplay );
	mmDialCallAndPlay->setMethod( this, (MethodMessageResponsePtr)&VGettyAMBundle::mmDoDialCallAndPlay );
	mmDialCallAndPlay->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );


	MethodMessageResponse* mmHangUpCall = Methods.create<MethodMessageResponse>( _Phone_METHOD_hangup );
	mmHangUpCall->setMethod( this, (MethodMessageResponsePtr)&VGettyAMBundle::mmDoHangUpCall );
	mmHangUpCall->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );

	MethodMessageResponse* mmAnswerCall = Methods.create<MethodMessageResponse>( _Phone_METHOD_answer );
	mmAnswerCall->setMethod( this, (MethodMessageResponsePtr)&VGettyAMBundle::mmDoAnswerCall );
	mmAnswerCall->getPermission().setMask( Permission::RX, Permission::RX, Permission::RX );

	setBundleState(Bundle::STATE_CREATED, false);
 }


VGettyAMBundle::~VGettyAMBundle() {
	Log(LOG_INFO, " Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~VGettyAMBundle() : Destroying while Bundle is running! Trying to stop.." );
		bundleStop();
	 }
	if ( this->devWorker->isThreadRunning() ) {
		Log(LOG_WARNING, "~VGettyAMBundle() : Destroying while RUNNING! Trying to stop.." );
		this->devWorker->quitThread();
		this->devWorker->waitThread(30000);
	 }
	delete this->devWorker; this->devWorker = NULL;
	Log(LOG_VERBOSE, ":~VGettyAMBundle() : Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT VGettyAMBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) ) return WRET_ERR_WRONG_STATE;

	BusDevices.setMessageHandler(this);
	WRESULT bus_connected = BusDevices.connect( _Bus_Devices );
	if ( WFAILED(bus_connected) )
		Log(LOG_CRITICAL, ":VGettyAMBundle() : FAILED#%d Connecting Bus ("_Bus_Devices")..", bus_connected );

	WRESULT ret = BundleGeneric::start_SynchThread( this->devWorker );

	if ( WSUCCEEDED(ret) ) {
		Log(LOG_VERBOSE, ":bundleStart() : Registering as Device.." );
		wosh::Device* myself = new wosh::Device( *this );
		myself->getInterfaces() = this->Interfaces;
		WoshKernel::getInstance()->devices().registerDevice( myself );
	 }

	declareCommunicator();

	// BUNDLE-STATE (STARTED) will be updated async by WORKER, through call: IThreadListener::thread_event()
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT VGettyAMBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) ) return WRET_ERR_WRONG_STATE;

	WRESULT ret = BundleGeneric::stop_SynchThread( this->devWorker );

	if ( WSUCCEEDED(ret) ) {
		Log(LOG_VERBOSE, ":bundleStop() : UnRegistering as Device.." );
		WoshKernel::getInstance()->devices().unRegisterDevice( this->getName() );
	 }
	// BUNDLE-STATE (STOPPED) will be updated async by WORKER, through call: IThreadListener::thread_event()
	return ret;
 }

WRESULT VGettyAMBundle::bundleDiagnostic() {
	Log(LOG_VERBOSE, ":bundleDiagnostic() : Starting VGettyAMBundle-Diagnostic" );

	WRESULT ret = BundleGeneric::bundleDiagnostic();

	Log(LOG_INFO, ":bundleDiagnostic() : Finished VGettyAMBundle-Diagnostic [%d]", ret );
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES

void VGettyAMBundle::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	// avoid message replicas
	if ( !MessageFilter::filterReplica( message, &BusCore ) )
		return;

	if ( message.getContent()->isNotification() ) {
		if ( message.getDestination().isBroadcast() )
			return;
		if ( !MessageFilter::isMessageTarget(message, this) )
			return;
		notify( message.getContent()->asNotification() );
		return;
	 }

	DeviceManager::busMessageForBundleDevices( message, source, this, this->Methods, this->Log, this->BusCore, this->BusDevices );
 }

//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS

bool VGettyAMBundle::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source )
 {
	if ( property_current.getKey() == _VGettyAM_KEY_RefreshFrequency ) {
		//Log(LOG_VERBOSE, " Changing Bundle Type to '%s'", property_new->getValue().toString().c_str() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_ScriptDial ) {
		this->devWorker->setDialScript( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_ScriptControl ) {
		this->devWorker->setCtrlScript( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_FolderStored ) {
		this->devWorker->setFolderMessageStored( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_FolderIncoming ) {
		this->devWorker->setFolderMessageIncoming( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_MessageGreeting ) {
		this->devWorker->setMessageGreeting( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_BindAddressSlave ) {
		this->devWorker->setBindAddressSlave( value_proposed.toString() );
		return true;
	 }
	else if ( property_current.getKey() == _VGettyAM_KEY_BindAddressDevice ) {
		this->devWorker->setBindAddressDevice( value_proposed.toString() );
		return true;
	 }
	return BundleGeneric::updatingProperty( do_update, value_proposed, property_current, source );
 }

///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void VGettyAMBundle::raiseEvent( MessageContent* content, SecurityToken* security )
 {
	Message* msg_event = new Message();
	msg_event->setSource( this );
	msg_event->setContent( content );
	msg_event->setDestinationBroadcast();
	if ( security != NULL )
		msg_event->setSecurityToken(security);
	else
		msg_event->setSecurityToken(generateCredentials());
	BusDevices.postMessage(msg_event);
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VGettyAMBundle::incoming_DTMF( int line, const std::string& sequence, VGettyAMImpl* )
 {
	// inject TEXT_MESSAGE into WOSH
	TextMessage* txtmsg = new TextMessage();
	txtmsg->setSMS();
	txtmsg->setRecipent_ID( Properties.getValueOf(_Phone_KEY_PhoneNumber).toString() );
	txtmsg->setRecipent_User( "wosh" );  // phone device is considered as a communicator, so target is always the kernel-user
	txtmsg->setPlain( sequence);

	raiseEvent( txtmsg, NULL );
 }

void VGettyAMBundle::incoming_Message( int line, const AudioMessage* message, VGettyAMImpl* )
 {
	///@todo EVAL USER/SOURCE vs. SECURITY RULES here

	// inject TEXT_MESSAGE into WOSH
	AudioMessage* audiomsg = message->clone();

	raiseEvent( audiomsg, NULL );
 }

void VGettyAMBundle::phone_event( int line, int event, VGettyAMImpl* )
 {
	if ( event == 0 )
		raiseEvent( new Fact(_Phone_EVENT_call_idle), NULL );
	else if ( event == 1 )
		raiseEvent( new Fact(_Phone_EVENT_call_incoming), NULL );

 }

/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS

/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

WRESULT VGettyAMBundle::declareCommunicator()
 {
	Table* table = new Table(6, 2);
	table->set<Variant>( _CommunicatorService_KEY_ChannelType, 0,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_ChannelType).clone(), 0,1);
	table->set<Variant>( _CommunicatorService_KEY_ChannelLink, 1,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_ChannelLink).clone(), 1,1);
	table->set<Variant>( _CommunicatorService_KEY_Cost, 2,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_Cost).clone(), 2,1);
	table->set<Variant>( _CommunicatorService_KEY_Priority, 3,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_Priority).clone(), 3,1);
	table->set<Variant>( _CommunicatorService_KEY_Reliability, 4,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_Reliability).clone(), 4,1);
	table->set<Variant>( _CommunicatorService_KEY_Protocol, 5,0);
	table->set( Properties.getValueOf(_CommunicatorService_KEY_Protocol).clone(), 5,1);

	Response* response = new Response( _METHOD_ListProperties, table );
	Message* msg_event = new Message(response);
	msg_event->setSource( this );
	msg_event->setDestinationBus(_Bus_Core);
	msg_event->getDestination().fromString("*:Communication");
	msg_event->setSecurityToken(generateCredentials() );
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

bool VGettyAMBundle::isUserReachable( const std::string& wosh_username )
 {
	std::string phonenumber = getPhoneNumberFromUsername( wosh_username );
	///@bug should at least validate number
	if ( phonenumber.size() > 8 )
		return true;
	return false;
 }

WRESULT VGettyAMBundle::sendMessageToUser( const std::string& wosh_username, const std::string& message )
 {
	std::string phonenumber = getPhoneNumberFromUsername(wosh_username);
	if ( phonenumber.size() == 0 ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "sendMessageToUser", "FAILED retrieving phonenumber of User %s", wosh_username.c_str() );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = sendMessageTo(phonenumber, message );
	return ret;
 }

WRESULT VGettyAMBundle::sendMessageTo( const std::string& user_phonenumber, const std::string& message ) {
	return WRET_ERR_INTERNAL;
 }

WRESULT VGettyAMBundle::notify( const Notification* notification )
 {
	if ( notification == NULL ) return WRET_ERR_PARAM;
	if ( notification->getNotificationType() != Notification::TYPE_AUDIO ) {
		return WRET_ERR_ILLEGAL_USE;
	 }
	if ( !notification->isKindOf<AudioMessage>() ) {
		return WRET_ERR_ILLEGAL_USE;
	 }
	const AudioMessage* audioNotif = dynamic_cast<const AudioMessage*>(notification);
	std::string filepath = audioNotif->getFile();

	std::string recipent = getPhoneNumberFromUsername( audioNotif->getRecipent_User() );
	fixPhoneNumber(recipent);

	WRESULT ret = this->devWorker->dialCallAndPlay( recipent, filepath );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string VGettyAMBundle::getPhoneNumberFromUsername( const std::string& wosh_username )
 {
	bool found = UserManager::isUser(wosh_username);
	if ( !found )
		return "";
	return UserManager::getUserProperty(wosh_username, "PhoneMobile").toString();
 }

std::string VGettyAMBundle::getUsernameFromPhoneNumber( const std::string& phone_number )
 {
	std::vector<std::string> users = UserManager::findUserByProperty( "PhoneMobile", phone_number );
	if ( users.size() == 0 )
		return "";
	return users[0];
 }

void VGettyAMBundle::fixPhoneNumber( std::string& phone ) const {
	if (phone.empty() ) return;
	if ( phone[0] != '+' )
		phone = "+" + phone;
	if (  phone[1] != '3' || phone[2] != '9' )
		phone = "+39" + phone.substr(1);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoDialCall( const Message* requestMessage )
 {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	std::string recipent = request->getArgument().toString();
	if ( recipent.size() < 5 )
		return request->replyResponse("Bad params", WRET_ERR_PARAM );
	Log(LOG_VERBOSE, "mmDoDialCall(%s)", recipent.c_str() );

	fixPhoneNumber(recipent);
	WRESULT ret = this->devWorker->dialCall( recipent );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoDialCallAndPlay( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Variant dUser;
	Variant dMessage;
	request->extractArguments( dUser, dMessage );
	if ( !dUser.isStringNotEmpty() || !dMessage.isStringNotEmpty() )
		return request->replyResponse("Invalid arguments [recipent, message]", WRET_ERR_PARAM );
	Log(LOG_VERBOSE, "mmDoDialCallAndPlay(%s, %s)", dUser.asString().c_str(), dMessage.asString().c_str() );
	fixPhoneNumber(dUser.asString());

	WRESULT ret = this->devWorker->dialCallAndPlay( dUser.asString(), dMessage.asString() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoHangUpCall( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	unsigned int call_id = request->getArgument().toInteger(0);
	Log(LOG_VERBOSE, "mmDoHangUpCall(%ld)", call_id );
	WRESULT ret = this->devWorker->hangUpCall( call_id );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
Response* VGettyAMBundle::mmDoAnswerCall( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	unsigned int call_id = request->getArgument().toInteger(0);
	Log(LOG_VERBOSE, "mmDoAnswerCall(%ld)", call_id );
	WRESULT ret = this->devWorker->answerCall( call_id );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoForceState( const Request* request ) {
	if ( request == NULL ) return NULL;
	std::string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoForceState(%s)", option.c_str() );
	return request->replyResponse(WRET_OK);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoSendMessageToUser( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	Variant dUser;
	Variant dMessage;
	request->extractArguments( dUser, dMessage );
	if ( !dUser.isStringNotEmpty() || !dMessage.isStringNotEmpty() )
		return request->replyResponse("Invalid arguments [username, message]", WRET_ERR_PARAM );
	std::string sender = requestMessage->getSecurityToken()->getUser();
	Log(LOG_VERBOSE, "mmDoSendMessageToUser(%s) : Sending %d chars by %s", dUser.asString().c_str(), dMessage.asString().size(), sender.c_str() );
	WRESULT ret = sendMessageToUser( dUser.asString(), dMessage.asString() );
	if ( WSUCCEEDED(ret) )
		return request->replyResponse( "Sent!" );
	return request->replyResponse("Internal Error!", ret );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* VGettyAMBundle::mmDoRaiseEvent( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dEventName;
	Variant dEventData;
	request->extractArguments( dEventName, dEventData );
	if ( !dEventName.isStringNotEmpty() || dEventData.isEmpty() )
		return request->replyResponse("Wrong DataType: DataList too short (!name)", WRET_ERR_PARAM );

	Log(LOG_VERBOSE, "mmDoFakeDeviceEvent() Fact: %s [%s]", dEventName.asString().c_str(), dEventData.toString().c_str() );
	raiseEvent( new Fact(dEventName.asString(), dEventData) );
	return request->replyResponse( "Device-Event (message) sent!" );
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace devices
}; // namespace wosh
